package pub.pjoc.util.random;

import pub.pjoc.util.Words;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.SecureRandom;
import java.util.Random;

/**
 * @author xiongyingqi
 * @since 16-12-7 上午11:05
 */
public abstract class RandomUtils {
  private static final Logger logger = LoggerFactory.getLogger(RandomUtils.class);

  public static RandomStringUtils newRandomStringUtils(boolean secure) {
    return new RandomStringUtils(secure);
  }

  public static class RandomStringUtils {
    RandomFactory randomFactory;

    public RandomStringUtils(boolean secure) {
      if (secure) {
        randomFactory = RandomFactory.useSecure();
      } else {
        randomFactory = RandomFactory.useUnsecure();
      }
    }

    /**
     * Produce random word strings.
     *
     * @param length length
     * @return words.
     */
    public String randomWordString(int length) {
      if (length <= 0) {
        return null;
      }
      return generateCharts(length, Words.words());
    }

    /**
     * Produce random number strings.
     *
     * @param length length
     * @return numbers.
     */
    public String randomNumberString(int length) {
      if (length <= 0) {
        return null;
      }
      return generateCharts(length, Words.numbers());
    }

    private String generateCharts(int length, char[] chars) {
      byte[] verifierBytes = new byte[length];
      randomFactory.newRandom().nextBytes(verifierBytes);
      return getAuthorizationCodeString(verifierBytes, chars);
    }


    /**
     * Convert these random bytes to a verifier string. The length of the byte array can be
     * . The default implementation mods the bytes to fit into the
     * ASCII letters 1-9, A-Z, a-z .
     *
     * @param verifierBytes The bytes.
     * @return The string.
     */
    private String getAuthorizationCodeString(byte[] verifierBytes, char[] salts) {
      char[] chars = new char[verifierBytes.length];
      for (int i = 0; i < verifierBytes.length; i++) {
        chars[i] = salts[((verifierBytes[i] & 0xFF) % salts.length)];
      }
      return new String(chars);
    }

  }

  static class RandomFactory {
    private Class<? extends Random> randomClass;

    private RandomFactory() {
    }

    public static RandomFactory useUnsecure() {
      RandomFactory randomFactory = new RandomFactory();
      randomFactory.randomClass = Random.class;
      return randomFactory;
    }

    public static RandomFactory useSecure() {
      RandomFactory randomFactory = new RandomFactory();
      randomFactory.randomClass = SecureRandom.class;
      return randomFactory;
    }

    public Random newRandom() {
      try {
        return randomClass.newInstance();
      } catch (InstantiationException e) {
        logger.error("", e);
      } catch (IllegalAccessException e) {
        logger.error("", e);
      }
      return new SecureRandom();
    }
  }
}
